| Conditions | 39 |
| Paths | 0 |
| Total Lines | 407 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 1 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like exports.publish often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | "use strict"; |
||
| 489 | exports.publish = function(taffyData, opts, tutorials) { |
||
| 490 | data = taffyData; |
||
| 491 | |||
| 492 | conf['default'] = conf['default'] || {}; |
||
| 493 | |||
| 494 | var templatePath = opts.template; |
||
| 495 | view = new template.Template(templatePath + '/tmpl'); |
||
| 496 | |||
| 497 | // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness |
||
| 498 | // doesn't try to hand them out later |
||
| 499 | // var indexUrl = helper.getUniqueFilename( 'index' ); |
||
| 500 | // don't call registerLink() on this one! 'index' is also a valid longname |
||
| 501 | |||
| 502 | // var globalUrl = helper.getUniqueFilename( 'global' ); |
||
| 503 | helper.registerLink('global', globalUrl); |
||
| 504 | |||
| 505 | // set up templating |
||
| 506 | // set up templating |
||
| 507 | view.layout = conf['default'].layoutFile ? |
||
| 508 | path.getResourcePath(path.dirname(conf['default'].layoutFile), |
||
| 509 | path.basename(conf['default'].layoutFile) ) : 'layout.tmpl'; |
||
| 510 | |||
| 511 | // set up tutorials for helper |
||
| 512 | helper.setTutorials(tutorials); |
||
| 513 | |||
| 514 | data = helper.prune(data); |
||
| 515 | |||
| 516 | var sortOption = navOptions.sort === undefined ? opts.sort : navOptions.sort; |
||
| 517 | sortOption = sortOption === undefined ? true : sortOption; |
||
| 518 | sortOption = sortOption === true ? 'longname, version, since' : sortOption; |
||
| 519 | if (sortOption) { |
||
| 520 | data.sort(sortOption); |
||
| 521 | } |
||
| 522 | helper.addEventListeners(data); |
||
| 523 | |||
| 524 | var sourceFiles = {}; |
||
| 525 | var sourceFilePaths = []; |
||
| 526 | data().each(function(doclet) { |
||
| 527 | doclet.attribs = ''; |
||
| 528 | |||
| 529 | if (doclet.examples) { |
||
| 530 | doclet.examples = doclet.examples.map(function(example) { |
||
| 531 | var caption, lang; |
||
| 532 | |||
| 533 | // allow using a markdown parser on the examples captions (surrounded by useless HTML p tags) |
||
| 534 | if (example.match(/^\s*(<p>)?<caption>([\s\S]+?)<\/caption>(\s*)([\s\S]+?)(<\/p>)?$/i)) { |
||
| 535 | caption = RegExp.$2; |
||
| 536 | example = RegExp.$4 + (RegExp.$1 ? '' : RegExp.$5); |
||
| 537 | } |
||
| 538 | |||
| 539 | var lang = /{@lang (.*?)}/.exec(example); |
||
| 540 | |||
| 541 | if (lang && lang[1]) { |
||
| 542 | example = example.replace(lang[0], ""); |
||
| 543 | lang = lang[1]; |
||
| 544 | |||
| 545 | } else { |
||
| 546 | lang = null; |
||
| 547 | } |
||
| 548 | |||
| 549 | return { |
||
| 550 | caption: caption || '', |
||
| 551 | code: example, |
||
| 552 | lang: lang || "javascript" |
||
| 553 | }; |
||
| 554 | }); |
||
| 555 | } |
||
| 556 | if (doclet.see) { |
||
| 557 | doclet.see.forEach(function(seeItem, i) { |
||
| 558 | doclet.see[i] = hashToLink(doclet, seeItem); |
||
| 559 | }); |
||
| 560 | } |
||
| 561 | |||
| 562 | // build a list of source files |
||
| 563 | var sourcePath; |
||
| 564 | if (doclet.meta) { |
||
| 565 | sourcePath = getPathFromDoclet(doclet); |
||
| 566 | sourceFiles[sourcePath] = { |
||
| 567 | resolved: sourcePath, |
||
| 568 | shortened: null |
||
| 569 | }; |
||
| 570 | |||
| 571 | //Check to see if the array of source file paths already contains |
||
| 572 | // the source path, if not then add it |
||
| 573 | if (sourceFilePaths.indexOf(sourcePath) === -1) { |
||
| 574 | sourceFilePaths.push(sourcePath) |
||
| 575 | } |
||
| 576 | } |
||
| 577 | }); |
||
| 578 | |||
| 579 | // update outdir if necessary, then create outdir |
||
| 580 | var packageInfo = (find({ |
||
| 581 | kind: 'package' |
||
| 582 | }) || [])[0]; |
||
| 583 | if (navOptions.disablePackagePath !== true && packageInfo && packageInfo.name) { |
||
| 584 | if (packageInfo.version) { |
||
| 585 | outdir = path.join(outdir, packageInfo.name, packageInfo.version); |
||
| 586 | } else { |
||
| 587 | outdir = path.join(outdir, packageInfo.name); |
||
| 588 | } |
||
| 589 | } |
||
| 590 | fs.mkPath(outdir); |
||
| 591 | |||
| 592 | // copy the template's static files to outdir |
||
| 593 | var fromDir = path.join( templatePath, 'static' ); |
||
| 594 | var staticFiles = fs.ls( fromDir, 3 ); |
||
| 595 | |||
| 596 | staticFiles.forEach( function ( fileName ) { |
||
| 597 | var toDir = fs.toDir( fileName.replace( fromDir, outdir ) ); |
||
| 598 | fs.mkPath( toDir ); |
||
| 599 | fs.copyFileSync( fileName, toDir ); |
||
| 600 | } ); |
||
| 601 | |||
| 602 | // copy user-specified static files to outdir |
||
| 603 | var staticFilePaths; |
||
| 604 | var staticFileFilter; |
||
| 605 | var staticFileScanner; |
||
| 606 | if (conf.default.staticFiles) { |
||
| 607 | // The canonical property name is `include`. We accept `paths` for backwards compatibility |
||
| 608 | // with a bug in JSDoc 3.2.x. |
||
| 609 | staticFilePaths = conf.default.staticFiles.include || |
||
| 610 | conf.default.staticFiles.paths || |
||
| 611 | []; |
||
| 612 | staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); |
||
| 613 | staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); |
||
| 614 | |||
| 615 | staticFilePaths.forEach(function(filePath) { |
||
| 616 | var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); |
||
| 617 | |||
| 618 | extraStaticFiles.forEach(function(fileName) { |
||
| 619 | var sourcePath = fs.toDir(filePath); |
||
| 620 | var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); |
||
| 621 | fs.mkPath(toDir); |
||
| 622 | fs.copyFileSync(fileName, toDir); |
||
| 623 | }); |
||
| 624 | }); |
||
| 625 | } |
||
| 626 | |||
| 627 | if (sourceFilePaths.length) { |
||
| 628 | var payload = navOptions.sourceRootPath; |
||
| 629 | if (!payload) { |
||
| 630 | payload = path.commonPrefix(sourceFilePaths); |
||
| 631 | } |
||
| 632 | sourceFiles = shortenPaths(sourceFiles, payload); |
||
| 633 | } |
||
| 634 | data().each(function(doclet) { |
||
| 635 | var url = helper.createLink(doclet); |
||
| 636 | helper.registerLink(doclet.longname, url); |
||
| 637 | |||
| 638 | // add a shortened version of the full path |
||
| 639 | var docletPath; |
||
| 640 | if (doclet.meta) { |
||
| 641 | docletPath = getPathFromDoclet(doclet); |
||
| 642 | if (!_.isEmpty(sourceFiles[docletPath])) { |
||
| 643 | docletPath = sourceFiles[docletPath].shortened; |
||
| 644 | if (docletPath) { |
||
| 645 | doclet.meta.shortpath = docletPath; |
||
| 646 | } |
||
| 647 | } |
||
| 648 | } |
||
| 649 | }); |
||
| 650 | |||
| 651 | data().each(function(doclet) { |
||
| 652 | var url = helper.longnameToUrl[doclet.longname]; |
||
| 653 | |||
| 654 | if (url.indexOf('#') > -1) { |
||
| 655 | doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); |
||
| 656 | } else { |
||
| 657 | doclet.id = doclet.name; |
||
| 658 | } |
||
| 659 | |||
| 660 | if (needsSignature(doclet)) { |
||
| 661 | addSignatureParams(doclet); |
||
| 662 | addSignatureReturns(doclet); |
||
| 663 | addAttribs(doclet); |
||
| 664 | } |
||
| 665 | }); |
||
| 666 | |||
| 667 | // do this after the urls have all been generated |
||
| 668 | data().each(function(doclet) { |
||
| 669 | doclet.ancestors = getAncestorLinks(doclet); |
||
| 670 | |||
| 671 | if (doclet.kind === 'member') { |
||
| 672 | addSignatureTypes(doclet); |
||
| 673 | addAttribs(doclet); |
||
| 674 | } |
||
| 675 | |||
| 676 | if (doclet.kind === 'constant') { |
||
| 677 | addSignatureTypes(doclet); |
||
| 678 | addAttribs(doclet); |
||
| 679 | doclet.kind = 'member'; |
||
| 680 | } |
||
| 681 | }); |
||
| 682 | |||
| 683 | var members = helper.getMembers(data); |
||
| 684 | members.tutorials = tutorials.children; |
||
| 685 | |||
| 686 | // add template helpers |
||
| 687 | view.find = find; |
||
| 688 | view.linkto = linkto; |
||
| 689 | view.resolveAuthorLinks = resolveAuthorLinks; |
||
| 690 | view.tutoriallink = tutoriallink; |
||
| 691 | view.htmlsafe = htmlsafe; |
||
| 692 | view.moment = moment; |
||
| 693 | |||
| 694 | // once for all |
||
| 695 | buildNav(members); |
||
| 696 | view.nav = navigationMaster; |
||
| 697 | view.navOptions = navOptions; |
||
| 698 | attachModuleSymbols(find({ |
||
| 699 | kind: ['class', 'function'], |
||
| 700 | longname: { |
||
| 701 | left: 'module:' |
||
| 702 | } |
||
| 703 | }), |
||
| 704 | members.modules); |
||
| 705 | |||
| 706 | // only output pretty-printed source files if requested; do this before generating any other |
||
| 707 | // pages, so the other pages can link to the source files |
||
| 708 | if (navOptions.outputSourceFiles) { |
||
| 709 | generateSourceFiles(sourceFiles); |
||
| 710 | } |
||
| 711 | |||
| 712 | if (members.globals.length) { |
||
| 713 | generate('global', 'Global', [{ |
||
| 714 | kind: 'globalobj' |
||
| 715 | }], globalUrl); |
||
| 716 | } |
||
| 717 | |||
| 718 | // some browsers can't make the dropdown work |
||
| 719 | if (view.nav.module && view.nav.module.members.length) { |
||
| 720 | generate('module', view.nav.module.title, [{ |
||
| 721 | kind: 'sectionIndex', |
||
| 722 | contents: view.nav.module |
||
| 723 | }], navigationMaster.module.link); |
||
| 724 | } |
||
| 725 | |||
| 726 | if (view.nav.class && view.nav.class.members.length) { |
||
| 727 | generate('class', view.nav.class.title, [{ |
||
| 728 | kind: 'sectionIndex', |
||
| 729 | contents: view.nav.class |
||
| 730 | }], navigationMaster.class.link); |
||
| 731 | } |
||
| 732 | |||
| 733 | if (view.nav.namespace && view.nav.namespace.members.length) { |
||
| 734 | generate('namespace', view.nav.namespace.title, [{ |
||
| 735 | kind: 'sectionIndex', |
||
| 736 | contents: view.nav.namespace |
||
| 737 | }], navigationMaster.namespace.link); |
||
| 738 | } |
||
| 739 | |||
| 740 | if (view.nav.mixin && view.nav.mixin.members.length) { |
||
| 741 | generate('mixin', view.nav.mixin.title, [{ |
||
| 742 | kind: 'sectionIndex', |
||
| 743 | contents: view.nav.mixin |
||
| 744 | }], navigationMaster.mixin.link); |
||
| 745 | } |
||
| 746 | |||
| 747 | if (view.nav.interface && view.nav.interface.members.length) { |
||
| 748 | generate('interface', view.nav.interface.title, [{ |
||
| 749 | kind: 'sectionIndex', |
||
| 750 | contents: view.nav.interface |
||
| 751 | }], navigationMaster.interface.link); |
||
| 752 | } |
||
| 753 | |||
| 754 | if (view.nav.external && view.nav.external.members.length) { |
||
| 755 | generate('external', view.nav.external.title, [{ |
||
| 756 | kind: 'sectionIndex', |
||
| 757 | contents: view.nav.external |
||
| 758 | }], navigationMaster.external.link); |
||
| 759 | } |
||
| 760 | |||
| 761 | if (view.nav.tutorial && view.nav.tutorial.members.length) { |
||
| 762 | generate('tutorial', view.nav.tutorial.title, [{ |
||
| 763 | kind: 'sectionIndex', |
||
| 764 | contents: view.nav.tutorial |
||
| 765 | }], navigationMaster.tutorial.link); |
||
| 766 | } |
||
| 767 | |||
| 768 | // index page displays information from package.json and lists files |
||
| 769 | var files = find({ |
||
| 770 | kind: 'file' |
||
| 771 | }), |
||
| 772 | packages = find({ |
||
| 773 | kind: 'package' |
||
| 774 | }); |
||
| 775 | |||
| 776 | generate('index', 'Index', |
||
| 777 | packages.concat( |
||
| 778 | [{ |
||
| 779 | kind: 'mainpage', |
||
| 780 | readme: opts.readme, |
||
| 781 | longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page' |
||
| 782 | }] |
||
| 783 | ).concat(files), |
||
| 784 | indexUrl); |
||
| 785 | |||
| 786 | // set up the lists that we'll use to generate pages |
||
| 787 | var classes = taffy(members.classes); |
||
| 788 | var modules = taffy(members.modules); |
||
| 789 | var namespaces = taffy(members.namespaces); |
||
| 790 | var mixins = taffy(members.mixins); |
||
| 791 | var interfaces = taffy(members.interfaces); |
||
| 792 | var externals = taffy(members.externals); |
||
| 793 | |||
| 794 | for (var longname in helper.longnameToUrl) { |
||
| 795 | if (hasOwnProp.call(helper.longnameToUrl, longname)) { |
||
| 796 | var myClasses = helper.find(classes, { |
||
| 797 | longname: longname |
||
| 798 | }); |
||
| 799 | if (myClasses.length) { |
||
| 800 | generate('class', 'Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); |
||
| 801 | } |
||
| 802 | |||
| 803 | var myModules = helper.find(modules, { |
||
| 804 | longname: longname |
||
| 805 | }); |
||
| 806 | if (myModules.length) { |
||
| 807 | generate('module', 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); |
||
| 808 | } |
||
| 809 | |||
| 810 | var myNamespaces = helper.find(namespaces, { |
||
| 811 | longname: longname |
||
| 812 | }); |
||
| 813 | if (myNamespaces.length) { |
||
| 814 | generate('namespace', 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); |
||
| 815 | } |
||
| 816 | |||
| 817 | var myMixins = helper.find(mixins, { |
||
| 818 | longname: longname |
||
| 819 | }); |
||
| 820 | if (myMixins.length) { |
||
| 821 | generate('mixin', 'Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); |
||
| 822 | } |
||
| 823 | |||
| 824 | var myInterfaces = helper.find(interfaces, { |
||
| 825 | longname: longname |
||
| 826 | }); |
||
| 827 | if (myInterfaces.length) { |
||
| 828 | generate('interface', 'Interface: ' + myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); |
||
| 829 | } |
||
| 830 | |||
| 831 | var myExternals = helper.find(externals, { |
||
| 832 | longname: longname |
||
| 833 | }); |
||
| 834 | if (myExternals.length) { |
||
| 835 | generate('external', 'External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); |
||
| 836 | } |
||
| 837 | } |
||
| 838 | } |
||
| 839 | |||
| 840 | // TODO: move the tutorial functions to templateHelper.js |
||
| 841 | function generateTutorial(title, tutorial, filename) { |
||
| 842 | var tutorialData = { |
||
| 843 | title: title, |
||
| 844 | header: tutorial.title, |
||
| 845 | content: tutorial.parse(), |
||
| 846 | children: tutorial.children, |
||
| 847 | docs: null |
||
| 848 | }; |
||
| 849 | |||
| 850 | var tutorialPath = path.join(outdir, filename), |
||
| 851 | html = view.render('tutorial.tmpl', tutorialData); |
||
| 852 | |||
| 853 | // yes, you can use {@link} in tutorials too! |
||
| 854 | html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> |
||
| 855 | |||
| 856 | if (searchEnabled) { |
||
| 857 | searchableDocuments[filename] = { |
||
| 858 | "id": filename, |
||
| 859 | "title": title, |
||
| 860 | "body": searchData(html) |
||
| 861 | }; |
||
| 862 | } |
||
| 863 | |||
| 864 | fs.writeFileSync(tutorialPath, html, 'utf8'); |
||
| 865 | } |
||
| 866 | |||
| 867 | // tutorials can have only one parent so there is no risk for loops |
||
| 868 | function saveChildren(node) { |
||
| 869 | node.children.forEach(function(child) { |
||
| 870 | generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); |
||
| 871 | saveChildren(child); |
||
| 872 | }); |
||
| 873 | } |
||
| 874 | |||
| 875 | function generateQuickTextSearch(templatePath, searchableDocuments, navOptions) { |
||
| 876 | var data = { |
||
| 877 | searchableDocuments: JSON.stringify(searchableDocuments), |
||
| 878 | navOptions: navOptions |
||
| 879 | }; |
||
| 880 | |||
| 881 | var tmplString = fs.readFileSync(templatePath + "/quicksearch.tmpl").toString(), |
||
| 882 | tmpl = _.template(tmplString); |
||
| 883 | |||
| 884 | var html = tmpl(data), |
||
| 885 | outpath = path.join(outdir, "quicksearch.html"); |
||
| 886 | |||
| 887 | fs.writeFileSync(outpath, html, "utf8"); |
||
| 888 | } |
||
| 889 | |||
| 890 | saveChildren(tutorials); |
||
| 891 | |||
| 892 | if (searchEnabled) { |
||
| 893 | generateQuickTextSearch(templatePath + '/tmpl', searchableDocuments, navOptions); |
||
| 894 | } |
||
| 895 | }; |
||
| 896 |